package com.fare.taluo.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

/**
 * 获得类型的辅助工具类
 * @author creator: xiangsf
 * @author modifior: xiangsf
 * @since 2012-12-19 下午1:51:22
 * @version v1.0
 */
public class ClassUtils extends org.apache.commons.lang.ClassUtils{
	
	protected static Logger logger = Logger.getLogger(ClassUtils.class);
	/**
	 * 获得泛型参数类型
	 * @param _clazz
	 * @return
	 */
	public static Class<?> getGenericType(Class<?> _clazz) {
		  Type genType = _clazz.getGenericSuperclass();
		  if (!(genType instanceof ParameterizedType)) {
			  throw new RuntimeException(String.format("类%s泛型参数出错!", _clazz.getCanonicalName()));
		  }
		  Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
		  if (!(params[0] instanceof Class)) {
			  throw new RuntimeException(String.format("类%s泛型参数出错!", _clazz.getCanonicalName()));
		  }
		  return (Class<?>)params[0];
	}
	/**
	 * 获得第N个泛型参数类型
	 * @param _clazz 需要获得泛型参数的原始类
	 * @param paramIndex 参数的索引
	 * @return
	 */
	public static Class<?> getGenericType(Class<?> _clazz, int paramIndex) {
		  Type genType = _clazz.getGenericSuperclass();
		  if (!(genType instanceof ParameterizedType)) {
			  throw new RuntimeException(String.format("类%s泛型参数出错!", _clazz.getCanonicalName()));
		  }
		  Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
		  if (params==null || params.length < paramIndex || !(params[paramIndex] instanceof Class)) {
			  throw new RuntimeException(String.format("类%s泛型参数出错!", _clazz.getCanonicalName()));
		  }
		  return (Class<?>)params[paramIndex];
	}
	
	/**
     * Attempts to create a class from a String.
     * @param className the name of the class to create.
     * @return the class.  CANNOT be NULL.
     * @throws IllegalArgumentException if the className does not exist.
     */
    @SuppressWarnings("unchecked")
    public static <T> Class<T> loadClass(final String className) throws IllegalArgumentException {
        try {
            return (Class<T>) Class.forName(className);
        } catch (final ClassNotFoundException e) {
            throw new IllegalArgumentException(className + " class not found.");
        }
    }


    /**
     * Creates a new instance of the given class by passing the given arguments
     * to the constructor.
     * @param className Name of class to be created.
     * @param args Constructor arguments.
     * @return New instance of given class.
     */
    public static <T> T newInstance(final String className, final Object ... args) {
        return newInstance(ClassUtils.<T>loadClass(className), args);
    }
    
    /**
     * Creates a new instance of the given class by passing the given arguments
     * to the constructor.
     * @param clazz Class of instance to be created.
     * @param args Constructor arguments.
     * @return New instance of given class.
     */
    public static <T> T newInstance(final Class<T> clazz, final Object ... args) {
        final Class<?>[] argClasses = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            argClasses[i] = args[i].getClass();
        }
        try {
            return clazz.getConstructor(argClasses).newInstance(args);
        } catch (final Exception e) {
            throw new IllegalArgumentException("Error creating new instance of " + clazz, e);
        }
    }

    /**
     * Gets the property descriptor for the named property on the given class.
     * @param clazz Class to which property belongs.
     * @param propertyName Name of property.
     * @return Property descriptor for given property or null if no property with given
     * name exists in given class.
     */
    public static PropertyDescriptor getPropertyDescriptor(final Class<?> clazz, final String propertyName) {
        try {
            return getPropertyDescriptor(Introspector.getBeanInfo(clazz), propertyName);
        } catch (final IntrospectionException e) {
            throw new RuntimeException("Failed getting bean info for " + clazz, e);
        }
    }

    /**
     * Gets the property descriptor for the named property from the bean info describing
     * a particular class to which property belongs.
     * @param info Bean info describing class to which property belongs.
     * @param propertyName Name of property.
     * @return Property descriptor for given property or null if no property with given
     * name exists.
     */
    public static PropertyDescriptor getPropertyDescriptor(final BeanInfo info, final String propertyName) {
        for (int i = 0; i < info.getPropertyDescriptors().length; i++) {
            final PropertyDescriptor pd = info.getPropertyDescriptors()[i];
            if (pd.getName().equals(propertyName)) {
                return pd;
            }
        }
        return null;
    }

    /**
     * Sets the given property on the target JavaBean using bean instrospection.
     * @param propertyName Property to set.
     * @param value Property value to set.
     * @param target Target java bean on which to set property.
     */
    public static void setProperty(final String propertyName, final Object value, final Object target) {
        try {
            setProperty(propertyName, value, target, Introspector.getBeanInfo(target.getClass()));
        } catch (final IntrospectionException e) {
            throw new RuntimeException("Failed getting bean info on target JavaBean " + target, e);
        }
    }

    /**
     * Sets the given property on the target JavaBean using bean instrospection.
     * @param propertyName Property to set.
     * @param value Property value to set.
     * @param target Target JavaBean on which to set property.
     * @param info BeanInfo describing the target JavaBean.
     */
    public static void setProperty(final String propertyName, final Object value, final Object target, final BeanInfo info) {
        try {
            final PropertyDescriptor pd = getPropertyDescriptor(info, propertyName);
            pd.getWriteMethod().invoke(target, value);
        } catch (final InvocationTargetException e) {
            throw new RuntimeException("Error setting property " + propertyName, e.getCause());
        } catch (final Exception e) {
            throw new RuntimeException("Error setting property " + propertyName, e);
        }
    }
    
    public static Object getBeanPropertyValue(Object beanObject, String propertyName){
    	PropertyDescriptor pd = getPropertyDescriptor(beanObject.getClass(),propertyName);
    	try {
    		Method m = pd.getReadMethod();
			return m != null ? m.invoke(beanObject, new Object[]{}) : null;
		} catch (Exception e) {
			logger.error("bean实体中没有获取到属性"+propertyName+"值,"+e.getMessage());
		}
    	return null;
    }
    
    public static Map<String, PropertyDescriptor> getAllDescriptorMap(final Class<?> clazz) throws Exception{
    	Map<String,PropertyDescriptor> fs = new HashMap<String,PropertyDescriptor>();
    	BeanInfo info = Introspector.getBeanInfo(clazz);
    	 for (int i = 0; i < info.getPropertyDescriptors().length; i++) {
             final PropertyDescriptor pd = info.getPropertyDescriptors()[i];
             if(!"class".equals(pd.getName())){
            	 fs.put(pd.getName(), pd);
             }
         }

    	return fs;
    }
    
    public static void copyProperties(Object dest,Object orig) throws Exception{
		Class oclz = orig.getClass();
		Class dclz = dest.getClass();
		Map<String,PropertyDescriptor> omap = getAllDescriptorMap(oclz);
		Map<String,PropertyDescriptor> dmap = getAllDescriptorMap(dclz);
		Set <String>dset = dmap.keySet();
		for(String fieldName:dset){
			PropertyDescriptor ofield = omap.get(fieldName);
			if(ofield!=null){
				Object value = ofield.getReadMethod().invoke(orig, new Object[]{});
				PropertyDescriptor dfield = dmap.get(fieldName);
				if(value!=null){
					dfield.getWriteMethod().invoke(dest, value);
				}
			}
		}
	}
}
